import { Server, Socket} from "socket.io";
import { EventEmitter } from "events";
import { SocketFrom, SocketMessage } from "ROOT/interface/ws";
import { MergeData } from "ROOT/trader/DataHandler";
import { Depth } from "ROOT/trader/OrderBook";
import { debugLogger, outLogger } from "ROOT/common/logger";

interface ServerToClientEvents {
  kline: (data: Omit<MergeData, 'bids' | 'asks'>) => void;
  depth: (data: Omit<Depth, 'timestamp'>) => void;
}

interface ClientToServerEvents {
  ['sub:kline']: (data: { symbol: string, period: string}) => void;
  ['unsub:kline']: (data: { symbol: string, period: string}) => void;
  ['sub:depth']: (data: { symbol: string, period: string}) => void;
  ['unsub:depth']: (data: { symbol: string, period: string}) => void;
}


export enum EventTypes {
    KLine = "kline",
    Depth = "depth",
}

interface EventData<T extends string, D> extends SocketMessage {
    type: T;
    from: SocketFrom.binance | SocketFrom.server;
    data: {
        symbol: string;
    } & D;
    period?: '15m' | '4h' | '1d';
}


export interface WSEvent {
    // 当前服务端转发到客户端的数据
    'send-to-client': EventData<
      EventTypes.KLine,
      Omit<MergeData, 'bids' | 'asks'>> 
      | EventData<EventTypes.Depth, Omit<Depth, 'timestamp'>
    >;
}



/**
 * 与客户端的socket
 */
export const socketIO = new Server<ClientToServerEvents, ServerToClientEvents>({
  path: "/socket.io",
  serveClient: false
});
const subCache: Record<string, string[]> = {};

function createJoin(type: 'kline' | 'depth', socket: Socket) {
  return function join(data: Parameters<ClientToServerEvents['sub:kline']>[0]) {
    debugLogger(`sub:${type} ${JSON.stringify(data)}`);
    if (data.symbol) {
      const roomId = type === 'kline' ? `${type}:${data.symbol}:${data.period}` : `${type}:${data.symbol}`;
      socket.join(roomId);
      if (subCache[socket.id].includes(roomId)) {
        return;
      }
      subCache[socket.id].push(roomId);
    }
  };
}
function leave(type: 'kline' | 'depth', socket: Socket) {
  return function join(data: Parameters<ClientToServerEvents['unsub:kline']>[0]) {
    debugLogger(`unsub:${type} ${JSON.stringify(data)}`);
    if (data.symbol) {
      const roomId = type === 'kline' ? `${type}:${data.symbol}:${data.period}` : `${type}:${data.symbol}`;
      const index = subCache[socket.id].indexOf(roomId);
      if (index > -1) {
        subCache[socket.id].splice(index, 1);
        socket.leave(roomId);
      }
    }
  };
}
socketIO.on("connection", function (socket) {
  subCache[socket.id] = [];
  socket.on("sub:kline", createJoin('kline', socket));
  socket.on("unsub:kline", leave('kline', socket));
  socket.on("sub:depth", createJoin('depth', socket));
  socket.on("unsub:depth", leave('depth', socket));
  socket.on("disconnect", reason => {
    subCache[socket.id].forEach(roomId => {
      socket.leave(roomId);
    });
  });
});

class Eventss extends EventEmitter {
  public emit!: <K extends keyof WSEvent>(
      event: K,
      arg: WSEvent[K]
  ) => boolean;
  public on!: <K extends keyof WSEvent>(
      event: K,
      listener: (arg: WSEvent[K]) => void
  ) => this;
}
// 自定义事件
export const ws_event = new EventEmitter() as Eventss;



ws_event.on("send-to-client", function (event) {
  if (event.from === SocketFrom.server || event.from === SocketFrom.binance) {
    if (event.type === EventTypes.KLine) {
      socketIO.to(`kline:${event.data.symbol}:${event.period}`).emit("kline", event.data);
    } else if(event.type === EventTypes.Depth) {
      socketIO.to(`depth:${event.data.symbol}`).emit("depth", event.data);
    }
  }
});
